
/* <<< COPYRIGHT START >>>
 * Copyright 2006-Present OxygenSoftwareLibrary.com
 * Licensed under the GNU Lesser General Public License.
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * @author: Ugorji Dick-Nwoke
 * <<< COPYRIGHT END >>>
 */


package oxygen.tool.classcompatibilityinspector;

import java.io.DataInputStream;
import java.io.IOException;

/** Adapted from code given to Ugorji by Anno Langen */
public class CCIParser {

  private String[] strings;
  private int[] p;
  private int[] d;
  private int[] refs;
  private int refCount;

  private void read(DataInputStream in) throws IOException {
    int n = in.readUnsignedShort();
    if (strings == null || strings.length < n) {
      strings = new String[n];
      p = new int[n];
      d = new int[n];
      refs = new int[2*n];
    }
    refCount = 0;
    for (int i = 1; i < n; i++) i = readEntry(in, i);
  }

  private int readEntry(DataInputStream in, int i) throws IOException {
    switch (in.readUnsignedByte()) {
    case 1: // CONSTANT_Utf8 
      strings[i] = in.readUTF();
      // System.out.println(i+" string "+strings[i]);
      break;
    case 3: // CONSTANT_Integer 
    case 4: // CONSTANT_Float 
      in.skipBytes(4);
      break;
    case 5: // CONSTANT_Long 
    case 6: // CONSTANT_Double 
      in.skipBytes(8);
      i++; // see http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#87125
      break;
    case 7: // CONSTANT_Class 
      p[i] = in.readUnsignedShort();
      // System.out.println(i+" class "+p[i]);
      break;
    case 8: // CONSTANT_String 
      in.skipBytes(2);
      break;
    case 9: // CONSTANT_Fieldref 
    case 10: // CONSTANT_Methodref 
    case 11: // CONSTANT_InterfaceMethodref 
      refs[refCount++] = in.readUnsignedShort();
      refs[refCount++] = in.readUnsignedShort();
      // System.out.println(i+" ref "+refs[refCount-2]+" "+refs[refCount-1]);
      break;
    case 12: // CONSTANT_NameAndType 
      p[i] = in.readUnsignedShort();
      d[i] = in.readUnsignedShort();
      break;
    }
    return i;
  }

  String getIName(int i) {return strings[p[i]];}

  String getName(int i) {return strings[i];}

  String getType(int i) {return strings[d[i]];}

  void parseRefs() {
    for (int i = 0; i < refCount;) {
      String cName = getIName(refs[i++]);
      int nti = refs[i++];
      String mName = getIName(nti);
      String type =  strings[d[nti]];
      if (mName.equals("<init>")) mName = cName;
      if (mName.indexOf('$') == -1) ref(cName, mName, type);
    }
  }

  public void parse(DataInputStream din) throws IOException {
    din.skipBytes(4); // magic
    int minor = din.readUnsignedShort();
    int major = din.readUnsignedShort(); 
    // System.out.println(major+"."+minor);
    read(din);
    int access = din.readUnsignedShort();
    String name = getIName(din.readUnsignedShort());
    className(name, access);
    din.skipBytes(2); // super_class
    din.skipBytes(2*din.readUnsignedShort()); // interfaces
    for (int n = din.readUnsignedShort(); n > 0; n--) {
      int fAccess = din.readUnsignedShort();
      String fName = getName(din.readUnsignedShort());
      String type = getName(din.readUnsignedShort());
      fieldName(fName, fAccess, type);
      skipAttributeInfos(din);
    }
    for (int n = din.readUnsignedShort(); n > 0; n--) {
      int mAccess = din.readUnsignedShort();
      String mName = getName(din.readUnsignedShort());
      String type = getName(din.readUnsignedShort());
      methodName(mName, mAccess, type);
      skipAttributeInfos(din);
    }
    parseRefs();
  }

  private void skipAttributeInfos(DataInputStream din) throws IOException {
    for (int n = din.readUnsignedShort(); n > 0; n--) {
      din.skipBytes(2); // name
      din.skipBytes(din.readInt());
    }
  }

  protected void className(String n, int access) {}
  protected void fieldName(String n, int access, String type) {}
  protected void methodName(String n, int access, String type) {}
  protected void ref(String classN, String member, String type) {}

}

